package com.warrior.core;


import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.JSONObject;
import com.warrior.Ecc;
import com.warrior.EosRpcService;
import com.warrior.api.vo.ChainInfo;
import com.warrior.api.vo.TableRows;
import com.warrior.api.vo.TableRowsReq;
import com.warrior.api.vo.account.Account;
import com.warrior.api.vo.action.Action;
import com.warrior.api.vo.action.ActionTrace;
import com.warrior.api.vo.action.Actions;
import com.warrior.api.vo.transaction.Transaction;
import com.warrior.api.vo.transaction.TransactionHistory;
import io.github.novacrypto.bip39.MnemonicGenerator;
import io.github.novacrypto.bip39.Words;
import io.github.novacrypto.bip39.wordlists.English;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import java.security.SecureRandom;
import java.util.ArrayList;
import java.util.LinkedHashMap;
import java.util.List;
import java.util.Map;

/**
 * 1.创建账户的费用计算： fee=1.005*内存（kb）*汇率
 */
public class EosApi {
    final static Logger log = LoggerFactory.getLogger(EosApi.class);
    final static String EOS_NAME = "EOS";
    private static final String EOS_TOKEN = "eosio.token";
    private static final String ACTION_TRANSFER = "transfer";
    public static final String K_mnemonic = "mnemonic";
    public static final String K_privateKey = "privateKey";
    public static final String K_publicKey = "publicKey";

    EosRpcService eosRpcService = null;

    public EosApi( ) {
    }

    public EosApi(String chainUrl) {
        eosRpcService = new EosRpcService(chainUrl);
    }

    /**
     * 1. 查询链信息
     *
     * @return
     */
    public ChainInfo getChainInfo() {
        try {
            return eosRpcService.getChainInfo();
        } catch (Exception e) {
            log.info("getAccount exception msg[{}]", e.getMessage());
            return null;
        }

    }

    /**
     * 2.创建账户
     *
     * @param pk                          :创建者私钥
     * @param creator：创建者
     * @param newAccount：新账户名（1.必须短于13个字符 2.仅能包含以下字符：.12345abcdefghijklmnopqrstuvwxyz）
     * @param owner：账户所有者
     * @param active：账户所有者
     * @param buyRam                      ：为新账户购买的内存
     * @return
     * @throws Exception
     */
    public Transaction createAccount(String pk, String creator, String newAccount, String owner, String active, long buyRam) throws Exception {
        return eosRpcService.createAccount(pk, creator, newAccount, owner, active, buyRam);
    }

    /**
     * 2.创建账户
     *
     * @param pk                          :创建者私钥
     * @param creator：创建者
     * @param newAccount：新账户名（1.必须短于13个字符 2.仅能包含以下字符：.12345abcdefghijklmnopqrstuvwxyz）
     * @param owner：账户所有者
     * @param active：账户所有者
     * @param buyRam                      ：为新账户购买的内存
     * @return
     * @throws Exception
     */
    public Transaction createAccount(String pk, String creator, String newAccount, String owner, String active, Long buyRam, String stakeNetQuantity, String stakeCpuQuantity, Long transfer) throws Exception {
        return eosRpcService.createAccount(pk, creator, newAccount, owner, active, buyRam,stakeNetQuantity,stakeCpuQuantity,transfer);
    }



    /**
     * 3.充值
     *
     * @param account：账户名
     * @param index：记录索引
     * @param pageSize    ：记录条数
     * @return
     */
    public boolean getActions(String account, int index, int pageSize) {
        //todo:修改
        try {
            Actions actions = eosRpcService.getActions(account, index, pageSize);
            if (actions != null) {
                List<Action> list = actions.getActions();
                if (list == null || list.size() == 0) {
                    return false;
                }
                //不可变更区块高度
                int lastIrreversibleBlock = actions.getLastIrreversibleBlock();
                //每次处理一条数据,不需要去重
                list = this.removeDuplicate(list);
                for (Action action : list) {
                    ActionTrace actionTrace = action.getActionTrace();
                    String eos_token = actionTrace.getAct().getAccount();
                    if (!EOS_TOKEN.equals(eos_token)) {
                        //log.info("非EOS交易记录：{}", account);
                        continue;
                    }
                    if (action.getBlockNum() > lastIrreversibleBlock) {
                        //log.info("未确认交易：{}", account);
                        continue;
                    }
                    String name = actionTrace.getAct().getName();
                    if (!ACTION_TRANSFER.equals(name)) {
                        //log.info("非EOS转账交易记录：{}", account);
                        continue;
                    }
                    //{from=eosqxyx11111, to=eosqxyx22222, quantity=10.0000 EOS, memo=test}
                    JSONObject json = JSONObject.parseObject(JSON.toJSONString(actionTrace.getAct().getData()));
                    if (!account.equals(json.get("to").toString())) {
                        //log.info("非充值记录：{}", actionTrace.getTrxId());
                        continue;
                    }
                    String[] quantity = json.get("quantity").toString().split(" ");
                    if (!EOS_NAME.equals(quantity[1])) {
                        //log.info("非EOS充值记录：{}", json.get("quantity"));
                        continue;
                    }
                    String memo = json.get("memo").toString();
                    /*if (StringUtils.isEmpty(memo)) {
                        log.info("记录memo为空");
                        continue;
                    }*/
                    log.info("充值{}-【{}】--【{}】", (index + 1), actionTrace.getTrxId(), json);
                    //判断是否存在用户,并添加充值记录
	                /*UserEntity user = userService.getUserById(Integer.parseInt(memo));
	                if (user == null) {
						log.info("用户信息不存在：memo：{}",memo);
						return true;
					}
	                record.setUserId(user.getUserId());
	                rechargeParse(record);*/

                    return true;
                }
            }
        } catch (Exception e1) {
            e1.printStackTrace();
            log.error("获取用户交易记录失败:{}", e1.getMessage());
        }
        return true;
    }

    /**
     * 3.充值
     *
     * @return
     */
    public List<Action> getActions(List<String> accounts) {
        List<Action> resultAction = new ArrayList<>();
        for (String account : accounts) {
            try {
                boolean isContinue = true;
                int pos = 0;
                int pageSize = 100;
                while (isContinue) {
                    Actions actions = eosRpcService.getActions(account, pos, pageSize);
                    pos += pageSize;
                    if (actions != null) {
                        List<Action> list = actions.getActions();
                        if (list == null || list.size() == 0) {
                            break;
                        }
                        //不可变更区块高度
                        int lastIrreversibleBlock = actions.getLastIrreversibleBlock();
                        //每次处理一条数据,不需要去重
                        list = this.removeDuplicate(list);
                        for (Action action : list) {
                            ActionTrace actionTrace = action.getActionTrace();
                            String eos_token = actionTrace.getAct().getAccount();
                            if (!EOS_TOKEN.equals(eos_token)) {
                                //log.info("非EOS交易记录：{}", account);
                                continue;
                            }
                            if (action.getBlockNum() > lastIrreversibleBlock) {
                                //log.info("未确认交易：{}", account);
                                continue;
                            }
                            String name = actionTrace.getAct().getName();
                            if (!ACTION_TRANSFER.equals(name)) {
                                //log.info("非EOS转账交易记录：{}", account);
                                continue;
                            }
                            //{from=eosqxyx11111, to=eosqxyx22222, quantity=10.0000 EOS, memo=test}
                            JSONObject json = JSONObject.parseObject(JSON.toJSONString(actionTrace.getAct().getData()));
                            if (!account.equals(json.get("to").toString())) {
                                //log.info("非充值记录：{}", actionTrace.getTrxId());
                                continue;
                            }
                            String[] quantity = json.get("quantity").toString().split(" ");
                            if (!EOS_NAME.equals(quantity[1])) {
                                //log.info("非EOS充值记录：{}", json.get("quantity"));
                                continue;
                            }
                            resultAction.add(action);
                        }
                    }
                }
            } catch (Exception e1) {
                e1.printStackTrace();
                log.error("获取用户交易记录失败:{}", e1.getMessage());
            }
        }
        return resultAction;
    }

    /**
     * 4.生成密钥对
     *
     * @throws Exception
     */
    public Map generateKey() {
        Map<String, String> resultMap = null;
        try {
            StringBuilder sb = new StringBuilder();
            byte[] entropy = new byte[Words.TWELVE.byteLength()];
            new SecureRandom().nextBytes(entropy);
            new MnemonicGenerator(English.INSTANCE)
                    .createMnemonic(entropy, sb::append);
            String mnemonic = sb.toString();
            String privateKey = Ecc.seedPrivate(mnemonic);
            String publicKey = Ecc.privateToPublic(privateKey);
            resultMap = new LinkedHashMap<>();
            resultMap.put(K_mnemonic, mnemonic);
            resultMap.put(K_privateKey, privateKey);
            resultMap.put(K_publicKey, publicKey);
        } catch (Exception e) {
            e.printStackTrace();
        }
        return resultMap;
    }

    /**
     * 查询余额
     *
     * @param accountName:账户名
     * @return
     */
    public double getBalance(String accountName) {
        double res = 0.00;
        try {
            List<String> list = eosRpcService.getCurrencyBalance(EOS_TOKEN, accountName, EOS_NAME);
            if (list != null && list.size() > 0) {
                res = Double.parseDouble(list.get(0).replaceAll(" EOS", ""));
            }
        } catch (Exception e) {
            e.printStackTrace();
        }
        return res;
    }


    /**
     * 转账
     *
     * @param pk                  ：秘钥
     * @param from                ：付款方
     * @param to：收款方
     * @param amount：金额（小数点后4位）
     * @param memo：备注
     * @return
     * @throws Exception
     */
    public Transaction transfer(String pk, String from, String to, double amount, String memo) throws Exception {
        String quantity = EosApi.toEosUtit(amount);
        return eosRpcService.transfer(pk, EOS_TOKEN, from, to, quantity, memo);
    }

    /**
     * 抵押资源
     * @param pk ：私钥
     * @param from ：抵押方
     * @param receiver ：资源接收方
     * @param stakeNetQuantity ：网络资源
     * @param stakeCpuQuantity ：cpu资源
     * @param transfer ：抵押类型（0租借1赠送）
     * @return
     * @throws Exception
     */
    public Transaction delegatebw(String pk, String from, String receiver, double stakeNetQuantity, double stakeCpuQuantity, Long transfer) throws Exception {
        String stakeNetQuantityStr = EosApi.toEosUtit(stakeNetQuantity);
        String stakeCpuQuantityStr = EosApi.toEosUtit(stakeCpuQuantity);
        return eosRpcService.delegatebw(pk,from,receiver,stakeNetQuantityStr,stakeCpuQuantityStr,transfer);
    }

    /**
     * 赎回资源
     * @param pk ：私钥
     * @param from ：抵押方
     * @param receiver ：资源接收方
     * @param stakeNetQuantity ：网络资源
     * @param stakeCpuQuantity ：cpu资源
     * @return
     * @throws Exception
     */
    public Transaction unDelegatebw(String pk, String from, String receiver, double stakeNetQuantity, double stakeCpuQuantity) throws Exception {
        String stakeNetQuantityStr = EosApi.toEosUtit(stakeNetQuantity);
        String stakeCpuQuantityStr = EosApi.toEosUtit(stakeCpuQuantity);
        return eosRpcService.unDelegatebw(pk,from,receiver,stakeNetQuantityStr,stakeCpuQuantityStr);
    }

    /**
     *购买内存
     * @param pk：私钥
     * @param payer ：支付者
     * @param receiver ：接受者
     * @param buyRam ：购买量（单位为字节）
     * @return
     * @throws Exception
     */
    public Transaction buyRam(String pk, String payer, String receiver, Long buyRam) throws Exception {
        return eosRpcService.buyRam(pk,payer,receiver,buyRam);
    }

    /**
     *卖出内存
     * @param pk：私钥
     * @param account ：支付者
     * @param buyRam ：购买量（单位为字节）
     * @return
     * @throws Exception
     */
    public Transaction sellRam(String pk, String account, Long buyRam) throws Exception {
        return eosRpcService.sellRam(pk,account,buyRam);
    }

    /**
     * 获取账户信息
     *
     * @param accountName:账户名
     * @return null代表账户不存在
     */
    public Account getAccount(String accountName) {
        try {
            Account account = eosRpcService.getAccount(accountName);
            return account;
        } catch (Exception e) {
            log.info("getAccount exception msg[{}]", e.getMessage());
            return null;
        }
    }


    /**
     * 获取交易历史
     *
     * @return null代表账户不存在
     */
    public TransactionHistory getTransaction(String txid) {
        try {
            TransactionHistory transactionHistory = eosRpcService.getTransaction(txid);
            return transactionHistory;
        } catch (Exception e) {
            log.info("getTranaction exception msg[{}]", e.getMessage());
            return null;
        }
    }

    //计算RAM实时价格,单位EOS/KB
    public double getRate() {
        double ramPrice = 0;
        try {
            TableRowsReq tableRowsReq = new TableRowsReq();
            tableRowsReq.setJson(true);
            tableRowsReq.setCode("eosio");
            tableRowsReq.setScope("eosio");
            tableRowsReq.setTable("rammarket");
            TableRows tableRows = eosRpcService.getTableRows(tableRowsReq);
            int n = 1;
            Map<String, String> tableRow = tableRows.getRows().get(0);
            Map<String, String> quote = JSON.parseObject(JSON.toJSONString(tableRow.get("quote")), Map.class);
            double quoteBalance = Double.parseDouble(quote.get("balance").split(" ")[0]);
            Map<String, String> base = JSON.parseObject(JSON.toJSONString(tableRow.get("base")), Map.class);
            double baseBalance = Double.parseDouble(base.get("balance").split(" ")[0]);
            ramPrice = (n * quoteBalance) / (n + baseBalance / 1024);
            System.out.println(ramPrice+" EOS/KB");
        }catch (Exception e){
            e.printStackTrace();
        }
        return ramPrice;

    }

    /**
     * 去重
     *
     * @param list
     * @return
     */
    public List<Action> removeDuplicate(List<Action> list) {
        for (int i = 0; i < list.size() - 1; i++) {
            for (int j = list.size() - 1; j > i; j--) {
                if (list.get(j).getActionTrace().getTrxId()
                        .equals(list.get(i).getActionTrace().getTrxId())) {
                    list.remove(j);
                }
            }
        }
        return list;
    }

    public static String toEosUtit(double num) {
        String numStr = String.valueOf(num);
        numStr += "0000";
        numStr = numStr.substring(0, numStr.indexOf(".")) + numStr.substring(numStr.indexOf("."), numStr.indexOf(".") + 5);
        numStr += " EOS";
        return numStr;
    }

    public static double toNumberUtit(String num) {
        double result = 0;
        try {
            result = Double.parseDouble(num.substring(0, num.indexOf(" ")).trim());
        } catch (Exception e) {
            e.printStackTrace();
        }
        return result;
    }

    public static void main(String[] args) {
        System.out.println(EosApi.toEosUtit(0.1));
    }
}
